
#include <string.h>
#include "ka-io/buffer.h"

using namespace io;

Buffer::Buffer(size_t size) 
: _data(NULL),
_size(0),
_swap(false) /*default to native order*/ {
    resize(size);
}

Buffer::~Buffer() {
    resize(0);
}

static void _put_32(uint8_t *d, const uint8_t *s) {
    d[0] = s[0];
    d[1] = s[1];
    d[2] = s[2];
    d[3] = s[3];
}

static void _put_swap_32(uint8_t *d, const uint8_t *s) {
    d[0] = s[3];
    d[1] = s[2];
    d[2] = s[1];
    d[3] = s[0];
}

static void _put_64(uint8_t *d, const uint8_t *s) {
    d[0] = s[0];
    d[1] = s[1];
    d[2] = s[2];
    d[3] = s[3];    
    d[4] = s[4];
    d[5] = s[5];
    d[6] = s[6];
    d[7] = s[7];
}

static void _put_swap_64(uint8_t *d, const uint8_t *s) {
    d[0] = s[7];
    d[1] = s[6];
    d[2] = s[5];
    d[3] = s[4];    
    d[4] = s[3];
    d[5] = s[2];
    d[6] = s[1];
    d[7] = s[0];    
}

static void _put_16(uint8_t *d, const uint8_t *s) {
    d[0] = s[0];
    d[1] = s[1];
}

static void _put_swap_16(uint8_t *d, const uint8_t *s) {
    d[0] = s[1];
    d[1] = s[0];
}

void Buffer::setOrder(uint32_t order) {
    _swap = (order != getNativeOrder());
}

uint32_t Buffer::getOrder() const {
    uint32_t order = getNativeOrder();
    if (_swap) {
        order = order ? BIG_ENDIAN : LITTLE_ENDIAN;
    }
    return order;
}

uint32_t Buffer::getNativeOrder() {
    union {
        uint32_t i;
        uint8_t b[4];
    } u;
    
    u.i = 0;
    u.b[0] = 1;
    
    return (u.i & UINT32_C(1));
}

void Buffer::putInt(size_t offset, int32_t v) {
    if (_swap) {
        _put_swap_32(_data+offset, (const uint8_t *)&v);        
    }
    else {
        _put_32(_data+offset, (const uint8_t *)&v);
    }
}

void Buffer::putUInt(size_t offset, uint32_t v) {
    if (_swap) {
        _put_swap_32(_data+offset, (const uint8_t *)&v);        
    }
    else {
        _put_32(_data+offset, (const uint8_t *)&v);
    }
}

void Buffer::putLong(size_t offset, int64_t v) {
    if (_swap) {
        _put_swap_64(_data+offset, (const uint8_t *)&v);        
    }
    else {
        _put_64(_data+offset, (const uint8_t *)&v);
    }
}

void Buffer::putULong(size_t offset, uint64_t v) {
    if (_swap) {
        _put_swap_64(_data+offset, (const uint8_t *)&v);        
    }
    else {
        _put_64(_data+offset, (const uint8_t *)&v);
    }
}

void Buffer::putShort(size_t offset, int16_t v) {
    if (_swap) {
        _put_swap_16(_data+offset, (const uint8_t *)&v);        
    }
    else {
        _put_16(_data+offset, (const uint8_t *)&v);
    }
}

void Buffer::putUShort(size_t offset, uint16_t v) {
    if (_swap) {
        _put_swap_16(_data+offset, (const uint8_t *)&v);        
    }
    else {
        _put_16(_data+offset, (const uint8_t *)&v);
    }
}

void Buffer::putByte(size_t offset, int8_t v) {
    *((uint8_t *)_data+offset) = v;
}

void Buffer::putUByte(size_t offset, uint8_t v) {
    _data[offset] = v;
}

void Buffer::putBytes(size_t offset, const void *source, size_t byteCount) {
    memmove(_data+offset, source, byteCount);
}

int32_t Buffer::getInt(size_t offset) {
    int32_t v;
    if (_swap) {
        _put_swap_32((uint8_t *)&v, _data+offset);
    }
    else {
        _put_32((uint8_t *)&v, _data+offset);
    }
    return v;
}

uint32_t Buffer::getUInt(size_t offset) {
    uint32_t v;
    if (_swap) {
        _put_swap_32((uint8_t *)&v, _data+offset);
    }
    else {
        _put_32((uint8_t *)&v, _data+offset);
    }
    return v;
}

int64_t Buffer::getLong(size_t offset) {
    int64_t v;
    if (_swap) {
        _put_swap_64((uint8_t *)&v, _data+offset);
    }
    else {
        _put_64((uint8_t *)&v, _data+offset);
    }
    return v;
    
}

uint64_t Buffer::getULong(size_t offset) {
    uint64_t v;
    if (_swap) {
        _put_swap_64((uint8_t *)&v, _data+offset);
    }
    else {
        _put_64((uint8_t *)&v, _data+offset);
    }
    return v;
}

int16_t Buffer::getShort(size_t offset) {
    int16_t v;
    if (_swap) {
        _put_swap_16((uint8_t *)&v, _data+offset);
    }
    else {
        _put_16((uint8_t *)&v, _data+offset);
    }
    return v;
}

uint16_t Buffer::getUShort(size_t offset) {
    uint16_t v;
    if (_swap) {
        _put_swap_16((uint8_t *)&v, _data+offset);
    }
    else {
        _put_16((uint8_t *)&v, _data+offset);
    }
    return v;
}

int8_t Buffer::getByte(size_t offset) {
    return *((int8_t *)_data+offset);
}

uint8_t Buffer::getUByte(size_t offset) {
    return _data[offset];
}

void Buffer::getBytes(size_t offset, void *dest, size_t byteCount) {
    memmove(dest, _data+offset, byteCount);
}

void Buffer::resize(size_t size) {
    if (_size > 0) {
        uint8_t * dat = (uint8_t *)realloc(_data, size);
        if (dat) {
            _data = dat;
            _size = size;
        }
    }
    else {
        free(_data);
        _data = NULL;
        _size = 0;
    }
}

uint8_t *Buffer::getDataPtr() {
    return _data;
}

const uint8_t * Buffer::getDataPtr() const {
    return _data;
}
